%==========================================================================
%=  This file is part of the Sound Restoration Project
%=  (c) Copyright Industrial Mathematics Institute
%=                University of South Carolina, Department of Mathematics
%=  ALL RIGHTS RESERVED
%=
%=  Author: Borislav Karaivanov
%==========================================================================

%==========================================================================
% List of the files on which this procedure depends:
%
% interpretInputArguments.m
% getAllSoundSignals.m
% getSoundTrackBounds.m
% estimateSoundTrackBounds.m
% getSoundTrackBoundsManually.m
% readImageFile.m
% readdpx.m
% writeToLogFile.m
% Image processing toolbox
% readImageByIndex.m
% extractSoundSignals.m
% write2DArrayToFile.m
% read2DArrayFromFile.m
% combine2DArraysFromFiles.m
% getSizeOf2DArrayFromFile.m
% extractSoundWithSoundOverlapsTest.m
% calibrateSignalsSingleMask.m
% findBestFitToSignal.m
% fitReduceFitSignal.m
% Curve Fitting toolbox
% visualizeSoundCalibrationSingleMask.m
% createMissingDirs.m
% computeOverlapGuess.m
% visualizeOverlapGuess.m
% computeSignalOverlaps.m
% findMinRelativeDifference.m
% checkSignalOverlapsAgainstImages.m
% combineSignals.m
% visualizeOverlaps.m
% partitionFileName.m
% saveSound.m
% imagesAndAudioToVideo.m
% imagesToVideo.m
% getFileSequenceFromRepresetative.m
% ffmpeg command line utilities
% tidyLogFile.m
%
%==========================================================================

%==========================================================================
% The function "extractForGUI" reads the sound track from a series of film
% scans and generates a single (mono) audio signal. It also produces a
% synchronized talking movie combining the audio and video.
% INPUT:  "inDirPath" is the full directory path to the image files.
% "inFileNamePrefixNoPath" is the common prefix in the names of the image
% files, i.e., the name less the full directory path, index field, and file
% extension.
% "outDirPath" is the full directory path to a directory where the final
% audio and video files, and the log file are to be written. If specified
% as empty, then its role is assumed by the directory holding the input
% image files.
% "scratchDirPath" is the full directory path to a scratch directory where
% all intermediate output files are to be written. If specified as empty,
% then its role is assumed by "outDirPath", the directory where the output
% files are to be written.
% "indFieldLength" is the length of the index field in the names of the
% image files.
% "inFileExt" is the extension of the image files.
% "fileIndArr" is a vector holding the indices of the image files to be
% used.
% "leftBound" and "rightBound" are the left and right vertical bounds of 
% the sound track region in the images (assumed the same for all images).
% "audioDelay" is a real number specifying the delay of the audio with
% respect to the video measured in number of frames. A negative value
% advances the audio.
% OUTPUT: "audioFileName" is an optional string returning the name of the
% audio file where the extracted sound is stored.
% "movieFileNameCellArr" is an optional cell vector of strings returning
% the names of the produced movies (synchronized video and audio).
%==========================================================================
function [audioFileName, movieFileNameCellArr] = extractForGUI(inDirPath, ...
    imageFileNamePrefixNoPath, outDirPath, scratchDirPath, indFieldLength, ...
    imageFileExt, fileIndArr, leftBound, rightBound, audioDelay)

if isempty(outDirPath)
    outDirPath = inDirPath;
end
if isempty(scratchDirPath)
    scratchDirPath = outDirPath;
end
% Initialize the output. Otherwise, in case of an early "return" (due to an
% interactive request to cancel) we will get a complaint that an output
% argument has not been assigned during the call to this function.
audioFileName = '';
movieFileNameCellArr = '';

% Write info to a log file.
writeToLogFile(['number of scans: ' num2str(numel(fileIndArr))]);

try
% Specify the algorithmic steps to be executed.
argInCellArr = {...
    'DoExtractSoundSignals', true, ...
    'DoExtractSoundWithSoundOverlaps', true, ...
    'DoSaveSound', true, ...
    'DoSyncAudioAndVideo', true};
[varStruct, doStruct] = interpretInputArguments(argInCellArr);

catch exception
    getReport(exception, 'extended', 'hyperlinks', 'on')
    audioFileName = '';
    return;
end

numPerBatch = varStruct.numPerBatch;
soundTextFileName = varStruct.soundTextFileName;
numSignalsPerMask = varStruct.numSignalsPerMask;
numSmoothings = varStruct.numSmoothings;
movingAveSpan = varStruct.movingAveSpan;
degOfPolyFitArr = varStruct.degOfPolyFitArr;
numToUseForGuess = varStruct.numToUseForGuess;
locMinNbhdRad = varStruct.locMinNbhdRad;
initSearchRad = varStruct.initSearchRad;
maxAmplitude = varStruct.maxAmplitude;
framesPerSecond = varStruct.framesPerSecond;
bitsPerSample = varStruct.bitsPerSample;



% Set up a progress bar with a "Cancel" button.
waitbarHandle = waitbar(0, ...
    sprintf('Extracting sound from %d files', numel(fileIndArr)), ...
    'CreateCancelBtn', 'setappdata(gcbf,''canceling'',1)' );
setappdata(waitbarHandle,'canceling',0)

% Specify the cleanup tasks to be performed at function completion.
cleanupObj = onCleanup(@()cleanup_extractForGUI(waitbarHandle));


partitionLenArr = [0.3, 0.025, 0.3, 0.025, 0.3, 0.05];
temp = numel(fileIndArr);
totalsArr = temp(ones(size(partitionLenArr)));
% Reduce the totals pairs.
totalsArr(2) = totalsArr(2) - 1;
totalsArr(3) = 0;
totalsArr(4) = totalsArr(4) - 2;
% Reduce the total for video files.
totalsArr(end) = 2;
waitbarStructArr = setWaitbar(waitbarHandle, partitionLenArr, totalsArr);
currWaitbarInd = 1;

%==========================================================================
if (doStruct.doExtractSoundSignals == true)
    tic
    
    % Extract the sound signals from all images and write those signals to
    % a text file.
    imageFileNamePrefix = [inDirPath imageFileNamePrefixNoPath];
    scratchFileNamePrefix = [scratchDirPath imageFileNamePrefixNoPath];
    imageBasedCalibMaskArr = '';
    
    lsj_imageFileNamePrefix = imageFileNamePrefix
    lsj_fileidx = fileIndArr(1)
    lsj_imageBasedCalibMaskArr = imageBasedCalibMaskArr
    lsj_numPerBatch = numPerBatch
    lsj_leftBound = leftBound
    lsj_rightBound = rightBound
    lsj_scratchFileNamePrefix = scratchFileNamePrefix
    lsj_indFieldLength = indFieldLength
    lsj_imageFileExt = imageFileExt

    soundTextFileName = getAllSoundSignals(imageFileNamePrefix, fileIndArr, ...
        imageBasedCalibMaskArr, numPerBatch, leftBound, rightBound, ...
        scratchFileNamePrefix, indFieldLength, imageFileExt, ...
        waitbarStructArr(currWaitbarInd));

    if getappdata(waitbarHandle,'canceling')
        return
    end

    currWaitbarInd = currWaitbarInd + 1;
    timeElapsed = toc;
    writeToLogFile(['Timing: sound signals extracted in ' num2str(timeElapsed, '%.2f') ' seconds']);
    fprintf(1, 'Timing: sound signals extracted in %f seconds.\n', timeElapsed);
end
%==========================================================================

lsj = 'here 1'
lsj_soundtextfile = soundTextFileName

%==========================================================================
if (doStruct.doExtractSoundWithSoundOverlaps == true)
    tic
    
    % Read in the signals from uncalibrated images from a text file.
    allSignalsArr = read2DArrayFromFile(soundTextFileName);
    signalArr = extractSoundWithSoundOverlapsTest(allSignalsArr, ...
        imageFileNamePrefix, fileIndArr, imageBasedCalibMaskArr, ...
        numSignalsPerMask, numSmoothings, movingAveSpan, degOfPolyFitArr, ...
        numToUseForGuess, initSearchRad, locMinNbhdRad, indFieldLength, ...
        imageFileExt, waitbarStructArr(currWaitbarInd:(currWaitbarInd + 2)));
    
    if getappdata(waitbarHandle,'canceling')
        return
    end

    currWaitbarInd = currWaitbarInd + 3;
    
%     % Read in the signals from calibrated images from a text file.
%     imageCalibAllSignalsArr = read2DArrayFromFile(imageCalibSoundTextFileName);
%     globalShotFileNamePrefix = [globalShotFileNamePrefix 'ImCalib_'];
%     imageCalibSignalArr = extractSoundWithSoundOverlapsTest(imageCalibAllSignalsArr, ...
%         imageFileNamePrefix, fileIndArr, imageBasedCalibMaskArr, ...
%         numSignalsPerMask, numSmoothings, movingAveSpan, degOfPolyFitArr, ...
%         numToUseForGuess, initSearchRad, locMinNbhdRad, indFieldLength, imageFileExt);
%     globalShotFileNamePrefix = globalShotFileNamePrefix(1:(end - 8));

    
%     allSignalsCalibArr = read2DArrayFromFile(imageCalibSoundTextFileName);
%     globalShotFileNamePrefix = [globalShotFileNamePrefix 'ImCalib_'];
%     signalCalibArr = extractSoundWithSoundOverlaps(allSignalsCalibArr, numSignalsPerMask, ...
%         numSmoothings, movingAveSpan);
%     globalShotFileNamePrefix = globalShotFileNamePrefix(1:(end - 8));
    
    timeElapsed = toc;
    writeToLogFile(['Timing: single sound signal with signal based overlaps produced in ' num2str(timeElapsed, '%.2f') ' seconds']);
    fprintf(1, 'Timing: single sound signal with signal based overlaps produced in %f seconds.\n', timeElapsed);
end
%==========================================================================

lsj = 'here 2'

%==========================================================================
if (doStruct.doSaveSound == true)
    tic
    
    % Get the name of the text sound file with directory path and file
    % extension removed.
    [~, soundTextFileNameNoPathNoExt] = partitionFileName(soundTextFileName);
    % Construct a file name prefix for the audio file.
    soundFileNamePrefix = [outDirPath soundTextFileNameNoPathNoExt '_SndOverlaps'];
    % Save the sound signal as an audio .wav file.
    samplingRate = floor(numel(signalArr)/(size(allSignalsArr, 2)/framesPerSecond));
    saveSound(signalArr, soundFileNamePrefix, samplingRate, maxAmplitude, bitsPerSample, samplingRate);
    saveSound(signalArr, soundFileNamePrefix, samplingRate, maxAmplitude, 24, 48000);
    saveSound(signalArr, soundFileNamePrefix, samplingRate, maxAmplitude, 24, 96000);
    audioFileName = saveSound(signalArr, soundFileNamePrefix, samplingRate, maxAmplitude, 16, 48000);

    if getappdata(waitbarHandle, 'canceling')
        return
    end

    timeElapsed = toc;
    writeToLogFile(['Timing: single sound signal saved in ' num2str(timeElapsed, '%.2f') ' seconds']);
    fprintf(1, 'Timing: single sound signal saved in %f seconds.\n', timeElapsed);
end
%==========================================================================



%==========================================================================
if (doStruct.doSyncAudioAndVideo == true)
    tic
    
    % Save the sound signal as an audio .wav file.
    imageFileName = [inDirPath imageFileNamePrefixNoPath num2str(fileIndArr(1), ...
        ['%0' num2str(indFieldLength) 'd']) '.' imageFileExt];
    if (ispc)
        videoFileSuffix = '.mp4';
        movieFileSuffix = '.mov';
    else
        videoFileSuffix = '.avi';
        movieFileSuffix = '.mov';
    end
    desiredSizesCellArr = {300; 1080};
    videoQuality = 75;
    movieFileNameCellArr = imagesAndAudioToMovie(audioFileName, imageFileName, ...
        fileIndArr(1), fileIndArr(end), outDirPath, movieFileSuffix, scratchDirPath, ...
        videoFileSuffix, audioDelay, framesPerSecond, desiredSizesCellArr, ...
        videoQuality, true, waitbarStructArr(currWaitbarInd:(currWaitbarInd + 1)));
    
    if getappdata(waitbarHandle,'canceling')
        return
    end

    timeElapsed = toc;
    writeToLogFile(['Timing: audio and video synchronized in ' num2str(timeElapsed, '%.2f') ' seconds']);
    fprintf(1, 'Timing: audio and video synchronized in %f seconds.\n', timeElapsed);
end
%==========================================================================


% Write info to a log file.
writeToLogFile(['index field length: ' num2str(indFieldLength)]);
writeToLogFile(['number of frames per second: ' num2str(framesPerSecond)]);

return;
end  % end of the function "extractForGUI"



%==========================================================================
% The cleanup routine called whenever the function "extractForGUI"
% 'return's.
%==========================================================================
function cleanup_extractForGUI(waitbarHandle)
% Get rid of the progress bar.
delete(waitbarHandle);

% Remove duplicate lines and group the remaining lines in the log file.
logFileName = writeToLogFile('End extract audio');
tidyLogFile(logFileName);

% Get rid of the global variables so that they are not available for any
% subsequently executed code.
clear global globalDoVisualize
clear global globalShotFileNamePrefix
clear global globalShotFileExt
clear global globalDoLog
clear global globalLogFileName

end  % end of the function "cleanup_extractForGUI"
